﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Trentacular.SharePoint.WSPUtil;

namespace Trentacular.SharePoint.StsAdmin
{
    [Serializable]
    public class DeploymentInventory : Dictionary<string, SolutionDeploymentInformation>
    {
        public DeploymentInventory() : base() { }

        public DeploymentInventory(int capacity) : base(capacity) { }

        protected DeploymentInventory(SerializationInfo info, StreamingContext context) : base(info, context) { }

        public bool HasFeatureDefinitions(SPFeatureScope scope)
        {
            return Values.FirstOrDefault(di => di.HasFeatureDefinitions(scope)) != null;
        }

        public bool HasFarmFeatureActivations
        {
            get
            {
                return Values.FirstOrDefault(di => di.HasFarmFeatureActivations) != null;
            }
        }

        public IEnumerable<WSPFeatureDefinition> GetDependentFeatures(Guid featureId)
        {
            return Values.SelectMany(di => di.FeatureDefinitions)
                .Where(fd => fd.ActivationDependencies.Contains(featureId));
        }

        public IEnumerable<WSPFeatureDefinition> GetInventoriedActivationDependencies(WSPFeatureDefinition featureDefinition)
        {
            return featureDefinition.ActivationDependencies
                .Select(id => FindFeatureDefinition(id))
                .Where(fd => fd != null);
        }

        private WSPFeatureDefinition FindFeatureDefinition(Guid featureId)
        {
            return Values.SelectMany(di => di.FeatureDefinitions).FirstOrDefault(fd => fd.Id.Equals(featureId));
        }
    }

    [Serializable]
    public class SolutionDeploymentInformation
    {
        public SolutionDeploymentInformation()
        {
            FeatureActivations = new FarmFeatureActivations();
        }

        public Guid SolutionId { get; set; }

        public bool Deployed { get; set; }
        public Guid[] DeployedWebApplications { get; set; }
        public IList<WSPFeatureDefinition> FeatureDefinitions { get; set; }

        public FarmFeatureActivations FeatureActivations { get; private set; }

        public bool HasFeatureDefinitions(SPFeatureScope scope)
        {
            return FeatureDefinitions.FirstOrDefault(fd => fd.Scope == scope) != null;
        }

        public bool HasFarmFeatureActivations
        {
            get
            {
                return FeatureActivations.ActivatedFeatures.Count > 0;
            }
        }

        public void AddDeactivatedFarmFeature(WSPFeatureDefinition featureDefinition)
        {
            FeatureActivations.DeactivatedFeatures.Add(featureDefinition);
        }

        public void AddFarmFeatureActivation(WSPFeatureDefinition featureDefinition)
        {
            FeatureActivations.ActivatedFeatures.Add(featureDefinition);
        }

        public void AddWebApplicationFeatureActivation(SPWebApplication webApp, WSPFeatureDefinition featureDefinition)
        {
            FeatureActivations.AddChildFeatureActivation(webApp.Id, featureDefinition);
        }

        public void AddSiteFeatureActivation(SPSite site, WSPFeatureDefinition featureDefinition)
        {
            FeatureActivations.EnsureChild(site.WebApplication.Id)
                .AddChildFeatureActivation(site.ID, featureDefinition);
        }

        public void AddWebFeatureActivation(SPWeb web, WSPFeatureDefinition featureDefinition)
        {
            var site = web.Site;
            FeatureActivations.EnsureChild(site.WebApplication.Id)
                .EnsureChild(site.ID)
                .AddChildFeatureActivation(web.ID, featureDefinition);
        }
    }

    [Serializable]
    public abstract class BaseFeatureActivations
    {
        public BaseFeatureActivations()
        {
            ActivatedFeatures = new List<WSPFeatureDefinition>();
        }

        public List<WSPFeatureDefinition> ActivatedFeatures { get; private set; }
    }

    [Serializable]
    public abstract class BaseParentFeatureActivations<TChild> : BaseFeatureActivations
        where TChild : BaseFeatureActivations
    {
        public BaseParentFeatureActivations()
        {
            Children = new Dictionary<Guid, TChild>();
        }

        public Dictionary<Guid, TChild> Children { get; private set; }

        public TChild EnsureChild(Guid childId)
        {
            if (!Children.ContainsKey(childId))
                Children[childId] = Activator.CreateInstance<TChild>();

            return Children[childId];
        }

        public void AddChildFeatureActivation(Guid childId, WSPFeatureDefinition featureDefinition)
        {
            EnsureChild(childId).ActivatedFeatures.Add(featureDefinition);
        }
    }

    [Serializable]
    public class FarmFeatureActivations : BaseParentFeatureActivations<WebApplicationFeatureActivations>
    {
        public FarmFeatureActivations()
        {
            DeactivatedFeatures = new List<WSPFeatureDefinition>();
        }

        public List<WSPFeatureDefinition> DeactivatedFeatures { get; private set; }
    }

    [Serializable]
    public class WebApplicationFeatureActivations : BaseParentFeatureActivations<SiteFeatureActivations> { }

    [Serializable]
    public class SiteFeatureActivations : BaseParentFeatureActivations<WebFeatureActivations> { }

    [Serializable]
    public class WebFeatureActivations : BaseFeatureActivations { }
}
